import { Client } from '@stomp/stompjs';
import type { IMessage } from '@stomp/stompjs';
import SockJS from 'sockjs-client';
import axios from '../utils/axios';
import { useUserStore } from '../stores/user';

export interface ChatMessage {
  id: number;
  conversationId: number;
  senderId: number;
  senderName: string;
  senderAvatar: string;
  recipientId: number;
  recipientName: string;
  recipientAvatar: string;
  content: string;
  sentTime: string;
  read: boolean;
  // File related fields
  fileUrl?: string;
  fileName?: string;
  fileType?: string;
  fileSize?: number;
}

export interface Conversation {
  id: number;
  user1: {
    id: number;
    name: string;
    avatar: string;
  };
  user2: {
    id: number;
    name: string;
    avatar: string;
  };
  lastMessage?: ChatMessage;
  lastMessageTime: string;
  unreadCount: number;
}

class ChatService {
  private client: Client | null = null;
  private connected: boolean = false;
  private connecting: boolean = false;
  private messageCallbacks: Map<string, Function[]> = new Map();
  private connectionTimeout: ReturnType<typeof setTimeout> | undefined = undefined;
  private reconnectAttempts: number = 0;
  private maxReconnectAttempts: number = 5;
  private reconnectDelay: number = 5000; // 5 seconds
  private pollingInterval: ReturnType<typeof setInterval> | undefined = undefined;

  /**
   * Initializes the chat service
   * @returns A Promise that resolves when the connection is successful
   */
  async init() {
    if (this.connecting || this.connected) return;
    
    try {
      this.connecting = true;
      console.log('Initializing chat service...');
      
      // Get token
      const userStore = useUserStore();
      const token = userStore.token;
      
      if (!token) {
        throw new Error('User not logged in, cannot connect to chat service');
      }
      
      // Set connection timeout
      this.connectionTimeout = setTimeout(() => {
        if (!this.connected) {
          console.error('WebSocket connection timed out, attempting polling mode');
          this.connecting = false;
          this.initPolling();
        }
      }, 10000); // 10 seconds timeout
      
      this.client = new Client({
        webSocketFactory: () => {
          // Use absolute path to ensure SockJS handles the proxy correctly
          console.log('Creating SockJS connection to: /ws');
          
          // Use complete SockJS options
          const socket = new SockJS('/ws', null, {
            transports: ['websocket', 'xhr-streaming', 'xhr-polling'],
            timeout: 5000,
          });
          
          // Add debug events
          socket.onopen = () => console.log('SockJS connection opened');
          socket.onclose = (event) => console.log('SockJS connection closed', event);
          socket.onerror = (error) => console.error('SockJS connection error', error);
          
          return socket;
        },
        connectHeaders: {
          Authorization: `Bearer ${token}`
        },
        reconnectDelay: this.reconnectDelay,
        heartbeatIncoming: 10000,
        heartbeatOutgoing: 10000,
        onConnect: this.onConnect.bind(this),
        onDisconnect: this.onDisconnect.bind(this),
        onStompError: this.onStompError.bind(this),
        // Add more debugging options
        debug: (msg) => {
          console.debug('STOMP debug:', msg);
        }
      });
      
      console.log('Activating STOMP client...');
      this.client.activate();
    } catch (error) {
      console.error('Failed to initialize chat service:', error);
      this.connecting = false;
      if (this.connectionTimeout) {
        clearTimeout(this.connectionTimeout);
      }
    }
  }
  
  /**
   * Initializes HTTP polling mode
   */
  private initPolling() {
    console.log('Using HTTP polling mode');
    if (this.pollingInterval) {
      clearInterval(this.pollingInterval);
    }
    
    // Check for new messages every 30 seconds
    this.pollingInterval = setInterval(async () => {
      try {
        const userStore = useUserStore();
        if (!userStore.isLoggedIn) {
          this.stopPolling();
          return;
        }
        
        // Get active conversation IDs
        const response = await axios.get('/api/chat/conversations');
        const conversations = response.data;
        
        if (conversations && conversations.length > 0) {
          // For each conversation, check for new messages
          conversations.forEach(async (conversation: Conversation) => {
            const messagesResponse = await axios.get(`/api/chat/conversations/${conversation.id}/messages`, {
              params: {
                page: 0,
                size: 20
              }
            });
            
            const messages = messagesResponse.data;
            if (messages && messages.length > 0) {
              const callbacks = this.messageCallbacks.get('message') || [];
              const conversationCallbacks = this.messageCallbacks.get(`conversation:${conversation.id}`) || [];
              
              // Call message callbacks, note that there may be duplicate notifications
              messages.forEach((message: ChatMessage) => {
                callbacks.forEach(callback => callback(message));
                conversationCallbacks.forEach(callback => callback(message));
              });
            }
          });
        }
      } catch (error) {
        console.error('Failed to poll messages:', error);
      }
    }, 30000); // 30 seconds
  }
  
  /**
   * Stop polling
   */
  private stopPolling() {
    if (this.pollingInterval) {
      clearInterval(this.pollingInterval);
      this.pollingInterval = undefined;
    }
  }
  
  // Connection success callback
  private onConnect() {
    console.log('WebSocket connection successful');
    this.connected = true;
    this.connecting = false;
    this.reconnectAttempts = 0;
    
    // Clear connection timeout
    if (this.connectionTimeout) {
      clearTimeout(this.connectionTimeout);
    }
    
    const userStore = useUserStore();
    const userId = userStore.userId;
    
    if (this.client && userId) {
      try {
        // Subscribe to private messages
        console.log(`Subscribing to message channel: /user/${userId}/queue/messages`);
        this.client.subscribe(`/user/${userId}/queue/messages`, this.onMessageReceived.bind(this));
        
        // Subscribe to error notifications
        console.log(`Subscribing to error channel: /user/${userId}/queue/errors`);
        this.client.subscribe(`/user/${userId}/queue/errors`, this.onErrorReceived.bind(this));
        
        // Send connection message
        console.log('Sending connection message to: /app/chat.connect');
        this.client.publish({
          destination: '/app/chat.connect'
        });
      } catch (error) {
        console.error('Failed to subscribe or send connection message:', error);
      }
    }
  }
  
  // Disconnection callback
  private onDisconnect() {
    console.log('WebSocket disconnected');
    this.connected = false;
    this.connecting = false;
    
    // Attempt to reconnect
    if (this.reconnectAttempts < this.maxReconnectAttempts) {
      this.reconnectAttempts++;
      console.log(`Attempting to reconnect (${this.reconnectAttempts}/${this.maxReconnectAttempts})...`);
      setTimeout(() => {
        this.init();
      }, this.reconnectDelay);
    } else {
      console.warn(`WebSocket reconnection failed, exceeding the maximum number of attempts (${this.maxReconnectAttempts}), switching to HTTP polling mode`);
      this.initPolling();
    }
  }
  
  // STOMP error callback
  private onStompError(frame: any) {
    console.error('STOMP error:', frame.headers['message']);
    console.error('STOMP error details:', frame.body);
  }
  
  // Message received callback
  private onMessageReceived(message: IMessage) {
    try {
      const chatMessage = JSON.parse(message.body) as ChatMessage;
      console.log('Received new message:', chatMessage);
      console.log(`Message details - ID: ${chatMessage.id}, Sender: ${chatMessage.senderId}, Conversation ID: ${chatMessage.conversationId}`);
      
      // Call all message callbacks
      const callbacks = this.messageCallbacks.get('message') || [];
      console.log(`Triggering ${callbacks.length} global message callbacks`);
      callbacks.forEach(callback => callback(chatMessage));
      
      // Call callbacks for the specific conversation
      if (chatMessage.conversationId) {
        const conversationCallbacks = this.messageCallbacks.get(`conversation:${chatMessage.conversationId}`) || [];
        console.log(`Triggering ${conversationCallbacks.length} conversation-specific callbacks (ID: ${chatMessage.conversationId})`);
        conversationCallbacks.forEach(callback => callback(chatMessage));
      }
    } catch (error) {
      console.error('Failed to process received message:', error);
    }
  }
  
  // Error notification received callback
  private onErrorReceived(message: IMessage) {
    try {
      const errorInfo = JSON.parse(message.body);
      console.error('Received error notification:', errorInfo);
      
      // Add error handling logic here, such as displaying a notification to the user
    } catch (error) {
      console.error('Failed to process error notification:', error);
    }
  }
  
  // Add message callback
  onMessage(callback: Function, conversationId?: number) {
    const key = conversationId ? `conversation:${conversationId}` : 'message';
    
    if (!this.messageCallbacks.has(key)) {
      this.messageCallbacks.set(key, []);
    }
    
    this.messageCallbacks.get(key)!.push(callback);
    return () => this.removeMessageCallback(callback, conversationId);
  }
  
  // Remove message callback
  private removeMessageCallback(callback: Function, conversationId?: number) {
    const key = conversationId ? `conversation:${conversationId}` : 'message';
    
    if (this.messageCallbacks.has(key)) {
      const callbacks = this.messageCallbacks.get(key)!;
      const index = callbacks.indexOf(callback);
      if (index !== -1) {
        callbacks.splice(index, 1);
      }
    }
  }
  
  // Send message
  async sendMessage(recipientId: number, content: string): Promise<void> {
    const userStore = useUserStore();
    if (!userStore.isLoggedIn || !userStore.userId) {
        console.error('Failed to send message: User not logged in or invalid ID');
        return Promise.reject(new Error('Failed to send message: User not logged in'));
    }

    if (!recipientId) {
        console.error('Failed to send message: Invalid recipient ID');
        return Promise.reject(new Error('Invalid recipient ID'));
    }

    // Validate content
    if (!content || content.trim() === '') {
        console.error('Failed to send message: Message content is empty');
        return Promise.reject(new Error('Message content cannot be empty'));
    }

    if (!this.connected || !this.client) {
        console.warn('WebSocket not connected, sending message using HTTP API');
        return this.sendMessageHttp(recipientId, content);
    }

    try {
        console.log(`Sending message via WebSocket - Sender ID: ${userStore.userId}, Recipient ID: ${recipientId}`);
        
        // Ensure user ID is a number
        const senderId = Number(userStore.userId);
        if (isNaN(senderId)) {
            throw new Error('Invalid user ID');
        }
        
        this.client.publish({
            destination: '/app/chat.sendMessage',
            body: JSON.stringify({
                senderId,
                recipientId,
                content
            })
        });

        return Promise.resolve();
    } catch (error) {
        console.error('Failed to send message:', error);
        // Fallback to HTTP API
        return this.sendMessageHttp(recipientId, content);
    }
  }
  
  // Send file message
  async sendFileMessage(recipientId: number, file: File, content: string = ''): Promise<void> {
    try {
      // Upload the file first
      const fileInfo = await this.uploadChatFile(file);
      
      // Then send the message containing the file information
      if (!this.connected || !this.client) {
        // Fallback to HTTP API
        return this.sendFileMessageHttp(recipientId, fileInfo, content);
      }
      
      const userStore = useUserStore();
      const senderId = Number(userStore.userId);
      
      this.client.publish({
        destination: '/app/chat.sendMessage',
        body: JSON.stringify({
          senderId,
          recipientId,
          content,
          fileUrl: fileInfo.fileUrl,
          fileName: fileInfo.fileName,
          fileType: fileInfo.fileType,
          fileSize: fileInfo.fileSize
        })
      });
      
      return Promise.resolve();
    } catch (error) {
      console.error('Failed to send file message:', error);
      throw error;
    }
  }
  
  // Send file message via HTTP API
  private async sendFileMessageHttp(recipientId: number, fileInfo: any, content: string = '') {
    try {
      const response = await axios.post('/api/chat/send', {
        recipientId,
        content,
        fileUrl: fileInfo.fileUrl,
        fileName: fileInfo.fileName,
        fileType: fileInfo.fileType,
        fileSize: fileInfo.fileSize
      });
      
      return response.data;
    } catch (error) {
      console.error('Failed to send file message via HTTP API:', error);
      throw error;
    }
  }
  
  // Upload chat file
  async uploadChatFile(file: File) {
    try {
      const formData = new FormData();
      formData.append('file', file);
      
      const response = await axios.post('/api/chat/upload-file', formData, {
        headers: {
          'Content-Type': 'multipart/form-data'
        }
      });
      
      return response.data;
    } catch (error) {
      console.error('Failed to upload chat file:', error);
      throw error;
    }
  }
  
  // Send message via HTTP API
  private async sendMessageHttp(recipientId: number, content: string) {
    try {
      console.log(`Sending message via HTTP API - Recipient ID: ${recipientId}`);
      
      // Add retry mechanism
      let retries = 0;
      const maxRetries = 2;
      
      while (retries <= maxRetries) {
        try {
          const response = await axios.post('/api/chat/send', {
            recipientId,
            content
          });
          
          console.log('HTTP message sent successfully');
          return response.data;
        } catch (error: any) {
          if (error.response && error.response.status === 401) {
            // Authentication issue, attempt to refresh user state
            const userStore = useUserStore();
            console.warn('Message sending failed, possibly due to authentication issue, attempting to refresh user state');
            
            if (retries < maxRetries) {
              retries++;
              await userStore.fetchUserInfo(true);
              continue;
            }
          }
          
          // Other errors or retry failures, throw exception
          throw error;
        }
      }
    } catch (error) {
      console.error('Failed to send message via HTTP API:', error);
      throw error;
    }
  }
  
  // Get conversation with user
  async getConversationWithUser(userId: number) {
    try {
      const response = await axios.get(`/api/chat/conversations/${userId}`);
      return response.data as Conversation;
    } catch (error) {
      console.error('Failed to get conversation with user:', error);
      throw error;
    }
  }
  
  // Get all conversations for current user
  async getConversations() {
    try {
      const response = await axios.get('/api/chat/conversations');
      return response.data as Conversation[];
    } catch (error) {
      console.error('Failed to get conversation list:', error);
      throw error;
    }
  }
  
  // Get unread message count
  async getUnreadMessageCount() {
    try {
      const response = await axios.get('/api/chat/unread/count');
      return response.data.count as number;
    } catch (error) {
      console.error('Failed to get unread message count:', error);
      return 0;
    }
  }
  
  // Get messages before a specific message ID
  async getMessagesBeforeId(conversationId: number, messageId: number, size: number = 20) {
    try {
      const response = await axios.get(
        `/api/chat/conversations/${conversationId}/messages/before/${messageId}`,
        { params: { size } }
      );
      return response.data as ChatMessage[];
    } catch (error) {
      console.error('Failed to get historical messages:', error);
      throw error;
    }
  }
  
  // Mark conversation as read
  async markConversationAsRead(conversationId: number) {
    try {
      const response = await axios.post(`/api/chat/conversations/${conversationId}/read`);
      return response.data.updatedCount as number;
    } catch (error) {
      console.error('Failed to mark conversation as read:', error);
      throw error;
    }
  }
  
  // Get messages for a conversation
  async getMessagesForConversation(conversationId: number, page: number = 0, size: number = 20) {
    try {
      const response = await axios.get(
        `/api/chat/conversations/${conversationId}/messages`,
        { params: { page, size } }
      );
      return response.data as ChatMessage[];
    } catch (error) {
      console.error('Failed to get conversation messages:', error);
      throw error;
    }
  }
  
  // Disconnect WebSocket connection
  disconnect() {
    if (this.client && (this.connected || this.connecting)) {
      console.log('Disconnecting WebSocket connection...');
      try {
        this.client.deactivate();
        this.connected = false;
        this.connecting = false;
        console.log('WebSocket connection disconnected');
      } catch (error) {
        console.error('An error occurred while disconnecting the WebSocket connection:', error);
      }
    }
    
    // Stop polling
    this.stopPolling();
  }
}

// Export singleton
export default new ChatService();
